﻿/* 2012/8/11 */
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using Cosmos.Shortcuts;
using Cosmos.IO.FileSystemPath;
using Cosmos.Drawing;

namespace Cosmos.Windows.Forms
{
    /// <summary>
    /// Represents a picture box control with scroll bars.
    /// </summary>
    [DefaultEvent("ImageChanged")]
    public partial class ScrollablePictureBox : UserControl
    {
        [Flags]
        protected enum Direction : int
        {
            Center = 0x0,
            North = 0x1,
            West = 0x2,
            South = 0x4,
            East = 0x8,
            NorthWest = 0x3,
            SouthWest = 0x6,
            NorthEast = 0x9,
            SouthEast = 0xC,
        };

        /// <summary>
        /// The scale of image zooming.
        /// </summary>
        private const double _IMAGE_ZOOM = 1.5;

        private const int _IMAGE_MOVE_INTERVAL = 200;
        private const int _SCROLL_BAR_TICK = 50;

        #region Image Fields

        /// <summary>
        /// The file path of the displayed image.
        /// </summary>
        private volatile string _imagePath;

        /// <summary>
        /// The image which is being displayed.
        /// </summary>
        private volatile Image _image;

        /// <summary>
        /// Original width of displayed image.
        /// </summary>
        private volatile int _imageWidth;

        /// <summary>
        /// Original height of displayed image.
        /// </summary>
        private volatile int _imageHeight;

        /// <summary>
        /// The scale of displayed image to original image.
        /// </summary>
        private double _imageDisplayScale;

        /// <summary>
        /// The X-coordinate of start point of displayed image.
        /// </summary>        
        private int _imageStartX;

        /// <summary>
        /// The Y-coordinate of start point of displayed image.
        /// </summary>
        private int _imageStartY;

        /// <summary>
        /// The width of displayed image.
        /// </summary>
        private int _imageDisplayWidth;

        /// <summary>
        /// The height of displayed image.
        /// </summary>
        private int _imageDisplayHeight;

        private DrawingInfo _imageDrawingInfo;

        #endregion

        #region Mouse Fields

        /// <summary>
        /// Whether the mouse is dragging.
        /// </summary>
        private bool _mouseDraggingPressing;

        /// <summary>
        /// Whether the mouse's middle button has been clicked.
        /// </summary>
        private bool _mouseMiddlePressed;

        /// <summary>
        /// The location point of mouse.
        /// </summary>
        private Point _mouseLocation;

        /// <summary>
        /// The direction of mouse moving after press the middle button.
        /// </summary>
        private Direction _mouseMoveDirection;

        /// <summary>
        /// The distance of mouse moving after press the middle button.
        /// </summary>
        private double _mouseMoveDistance;

        /// <summary>
        /// A instance of MouseGestureParser class used to parse the mouse gesture of the picture box.
        /// </summary>
        private MouseGestureParser _mouseGestureParser;

        #endregion

        /// <summary>
        /// Initializes a new instance of ScrollablePictureBox class.
        /// </summary>
        public ScrollablePictureBox()
        {
            InitializeComponent();

            hScrollBar.MouseWheel += hScrollBar_MouseWheel;
            vScrollBar.MouseWheel += vScrollBar_MouseWheel;

            _imagePath = null;
            _image = null;
            _imageDisplayScale = 1.0;
            _imageWidth = 0;
            _imageHeight = 0;
            _imageStartX = 0;
            _imageStartY = 0;

            _mouseDraggingPressing = false;
            _mouseMiddlePressed = false;

            _mouseGestureParser = new MouseGestureParser();
            _mouseGestureParser.GestureCompleted += delegate(object sender, EventArgs _e)
            {
                MouseGestureParser self = sender as MouseGestureParser;
                OnMouseGestureCompleted(new MouseGestureEventArgs(self.Gesture));
            };
            _mouseGestureParser.Register(pictureBox);
        }

        /// <summary>
        /// Loads and displays the image whose file path has been set.
        /// </summary>
        public void LoadImage()
        {
            if (backgroundWorker.IsBusy)
            {
                backgroundWorker.CancelAsync();
            }
            else
            {
                if (FilePath.IsStaticImageFile(_imagePath))
                {
                    backgroundWorker.RunWorkerAsync();
                }
            }
        }

        /// <summary>
        /// Loads and displays a image with specified image file path.
        /// </summary>
        /// <param name="imagePath">File path of the image file.</param>
        public void LoadImage(string imagePath)
        {
            if (_imagePath != null && _imagePath.Equals(imagePath))
            {
                return;
            }
            else if (imagePath == null)
            {
                ClearImage();
            }
            else
            {
                _imagePath = imagePath;
                LoadImage();
            }
        }

        /// <summary>
        /// Clear the image displayed in ScrollablePictureBox.
        /// </summary>
        public void ClearImage()
        {
            _imagePath = null;
            if (_image != null)
            {
                _image.Dispose();
                _image = null;
            }

            _imageDisplayScale = 1.0;
            _imageWidth = 0;
            _imageHeight = 0;
            hScrollBar.Visible = false;
            vScrollBar.Visible = false;
            pictureBox.Refresh();
            OnImageChanged(EventArgs.Empty);
        }

        /// <summary>
        /// Display the image by original if the image is smaller than form,
        /// else display the image by the size of full form.
        /// </summary>
        public void CenterForm()
        {
            if (_image == null)
                return;

            hScrollBar.Visible = false;
            vScrollBar.Visible = false;
            _imageDrawingInfo = DrawingInfoProvider.Center(_image.Size, pictureBox.Size);
            _imageDisplayScale = _imageDrawingInfo.DisplayScale;
            _imageDisplayWidth = _imageDrawingInfo.DisplayWidth;
            _imageDisplayHeight = _imageDrawingInfo.DisplayHeight;
            _imageStartX = _imageDrawingInfo.CenterStartX;
            _imageStartY = _imageDrawingInfo.CenterStartY;

            OnScaleChanged(EventArgs.Empty);
            pictureBox.Refresh();
        }

        /// <summary>
        /// Display the image by the size that fix the form.
        /// </summary>
        public void FitForm()
        {
            if (_image == null)
                return;

            hScrollBar.Visible = false;
            vScrollBar.Visible = false;
            _imageDrawingInfo = DrawingInfoProvider.Fit(_image.Size, pictureBox.Size);
            _imageDisplayScale = _imageDrawingInfo.DisplayScale;
            _imageDisplayWidth = _imageDrawingInfo.DisplayWidth;
            _imageDisplayHeight = _imageDrawingInfo.DisplayHeight;
            _imageStartX = _imageDrawingInfo.CenterStartX;
            _imageStartY = _imageDrawingInfo.CenterStartY;

            OnScaleChanged(EventArgs.Empty);
            pictureBox.Refresh();
        }

        /// <summary>
        /// Display the image by the size of full form.
        /// </summary>
        public void FillForm()
        {
            if (_image == null)
                return;

            double widthScale = (double)pictureBox.Width / (double)_imageWidth;
            double heightScale = (double)pictureBox.Height / (double)_imageHeight;
            if (widthScale > heightScale)
            {
                hScrollBar.Visible = false;
                vScrollBar.Visible = true;
            }
            else if (heightScale > widthScale)
            {
                hScrollBar.Visible = true;
                vScrollBar.Visible = false;
            }
            else
            {
                hScrollBar.Visible = false;
                vScrollBar.Visible = false;
            }

            _imageDrawingInfo = DrawingInfoProvider.Fill(_image.Size, pictureBox.Size);
            _imageDisplayScale = _imageDrawingInfo.DisplayScale;
            _imageDisplayWidth = _imageDrawingInfo.DisplayWidth;
            _imageDisplayHeight = _imageDrawingInfo.DisplayHeight;
            _imageStartX = _imageDrawingInfo.CenterStartX;
            _imageStartY = _imageDrawingInfo.CenterStartY;
            InitializeScrollBar();

            OnScaleChanged(EventArgs.Empty);
            pictureBox.Refresh();
        }

        /// <summary>
        /// Change the scale of image displayed by specified ratio.
        /// </summary>
        /// <param name="scaleRatio">The ratio of new scale to old scale.</param>
        public void SetImageScaleBy(double scaleRatio)
        {
            if (_imagePath == null)
                return;

            // Calculate the central point of image in the pictureBox.
            int centerX;
            int centerY;
            int boxWidth = pictureBox.Width;
            int boxHeight = pictureBox.Height;

            centerX = boxWidth / 2 - _imageStartX;
            centerY = boxHeight / 2 - _imageStartY;

            // Calculate the new start point of drawing.
            _imageDisplayScale *= scaleRatio;
            centerX = (int)((double)centerX * scaleRatio);
            centerY = (int)((double)centerY * scaleRatio);
            _imageStartX = boxWidth / 2 - centerX;
            _imageStartY = boxHeight / 2 - centerY;

            AdjustPictureBox();           

            OnScaleChanged(EventArgs.Empty);
            pictureBox.Refresh();
        }

        /// <summary>
        /// Set scale of displayed image to specified value.
        /// </summary>
        /// <param name="scale">Scale of displayed image.</param>
        public void SetImageScaleTo(double scale)
        {
            SetImageScaleBy(scale / _imageDisplayScale);
        }

        /// <summary>
        /// Zoom out image by default ratio.
        /// </summary>
        public void ZoomOutImage()
        {
            ZoomOutImage(_IMAGE_ZOOM);
        }

        /// <summary>
        /// Zoom out image by specified ratio.
        /// </summary>
        /// <param name="zoomOutRatio">Zoom out ratio.</param>
        public void ZoomOutImage(double zoomOutRatio)
        {
            SetImageScaleBy(1 / zoomOutRatio);
        }

        /// <summary>
        /// Zoom in image by default ratio.
        /// </summary>
        public void ZoomInImage()
        {
            ZoomInImage(_IMAGE_ZOOM);
        }

        /// <summary>
        /// Zoom in image by specified ratio.
        /// </summary>
        /// <param name="zoomInRatio">Zoom in ratio.</param>
        public void ZoomInImage(double zoomInRatio)
        {
            SetImageScaleBy(zoomInRatio);
        }

        /// <summary>
        /// Display the image by original size.
        /// </summary>
        public void OriginalImage()
        {
            SetImageScaleBy(1 / _imageDisplayScale);
        }

        private int GetImageAreaWidth()
        {
            return Width - Padding.Horizontal - pictureBox.Margin.Horizontal;
        }

        private int GetImageAreaHeight()
        {
            return Height - Padding.Vertical - pictureBox.Margin.Vertical;
        }

        private void AdjustPictureBox()
        {
            while (true)
            {
                _imageDrawingInfo = DrawingInfoProvider.Zoom(_image.Size, pictureBox.Size, _imageDisplayScale);
                if (!SetScrollBarVisible())
                    break;
            }

            _imageDisplayWidth = _imageDrawingInfo.DisplayWidth;
            _imageDisplayHeight = _imageDrawingInfo.DisplayHeight;
            AdjustImageLocation();
            InitializeScrollBar();
        }

        /// <summary>
        /// Adjust image location, ensure the image is drawn at correct extent.
        /// If the image is smaller than picture box, the image should be drawn at center.
        /// </summary>
        private void AdjustImageLocation()
        {
            if (_imageDisplayWidth > pictureBox.Width)
            {
                if (_imageStartX < _imageDrawingInfo.MinStartX)
                {
                    _imageStartX = _imageDrawingInfo.MinStartX;
                }
                else if (_imageStartX > _imageDrawingInfo.MaxStartX)
                {
                    _imageStartX = _imageDrawingInfo.MaxStartX;
                }
            }
            else
            {
                if (_imageStartX != _imageDrawingInfo.CenterStartX)
                    _imageStartX = _imageDrawingInfo.CenterStartX;
            }

            if (_imageDisplayHeight > pictureBox.Height)
            {
                if (_imageStartY < _imageDrawingInfo.MinStartY)
                {
                    _imageStartY = _imageDrawingInfo.MinStartY;
                }
                else if (_imageStartY > _imageDrawingInfo.MaxStartY)
                {
                    _imageStartY = _imageDrawingInfo.MaxStartY;
                }
            }
            else
            {
                if (_imageStartY != _imageDrawingInfo.CenterStartY)
                    _imageStartY = _imageDrawingInfo.CenterStartY;
            }
        }

        /// <summary>
        /// Initializes scroll bar for current displayed image.
        /// </summary>
        private void InitializeScrollBar()
        {
            hScrollBar.Minimum = -_imageDrawingInfo.MaxStartX;
            hScrollBar.Maximum = -_imageDrawingInfo.MinStartX + pictureBox.Width - 1;
            hScrollBar.Value = -_imageStartX;
            hScrollBar.SmallChange = _imageDrawingInfo.DisplayWidth / _SCROLL_BAR_TICK;
            hScrollBar.LargeChange = pictureBox.Width;

            vScrollBar.Minimum = -_imageDrawingInfo.MaxStartY;
            vScrollBar.Maximum = -_imageDrawingInfo.MinStartY + pictureBox.Height - 1;
            vScrollBar.Value = -_imageStartY;
            vScrollBar.SmallChange = _imageDrawingInfo.DisplayHeight / _SCROLL_BAR_TICK;
            vScrollBar.LargeChange = pictureBox.Height;
        }

        /// <summary>
        /// Sets visible of scroll bars depending on displayed image size.
        /// </summary>
        /// <returns>Whether visible of any scroll bar has been changed.</returns>
        private bool SetScrollBarVisible()
        {
            bool isHScrollBarVisible = hScrollBar.Visible;
            if (_imageDrawingInfo.DisplayWidth > pictureBox.Width)
                hScrollBar.Visible = true;
            else
                hScrollBar.Visible = false;

            bool isVScrollBarVisible = vScrollBar.Visible;
            if (_imageDrawingInfo.DisplayHeight > pictureBox.Height)
                vScrollBar.Visible = true;
            else
                vScrollBar.Visible = false;

            return (isHScrollBarVisible != hScrollBar.Visible)
                || (isVScrollBarVisible != vScrollBar.Visible);
        }

        private void SetScrollBarValue()
        {
            // If the scroll bar is visible, set the value of scroll bar.
            if (_imageDisplayWidth > pictureBox.Width)
            {
                if (hScrollBar.Value != -_imageStartX)
                    hScrollBar.Value = -_imageStartX;
            }

            if (_imageDisplayHeight > pictureBox.Height)
            {
                if (vScrollBar.Value != -_imageStartY)
                    vScrollBar.Value = -_imageStartY;
            }
        }

        /// <summary>
        /// Move displayed image by specified internal.
        /// </summary>
        /// <param name="intervalX">Horizontal interval.</param>
        /// <param name="intervalY">Vertical interval.</param>
        public void MoveImage(int intervalX, int intervalY)
        {
            int formerStartX = _imageStartX;
            int formerStartY = _imageStartY;

            _imageStartX += intervalX;
            _imageStartY += intervalY;

            AdjustImageLocation();
            SetScrollBarValue();

            if (_imageStartX != formerStartX || _imageStartY != formerStartY)
                pictureBox.Refresh();
        }

        /// <summary>
        /// Move displayed image by specified interval and direction.
        /// </summary>
        /// <param name="interval">Interval of moving.</param>
        /// <param name="direction">Direction of moving.</param>
        private void MoveImage(double interval, Direction direction)
        {
            int intervalSide = (int)Math.Sqrt(interval * interval / 2);
            switch (direction)
            {
                case Direction.East:
                    MoveImage((int)-interval, 0); break;
                case Direction.West:
                    MoveImage((int)interval, 0); break;
                case Direction.South:
                    MoveImage(0, (int)-interval); break;
                case Direction.North:
                    MoveImage(0, (int)interval); break;
                case Direction.SouthEast:
                    MoveImage(-intervalSide, -intervalSide); break;
                case Direction.SouthWest:
                    MoveImage(intervalSide, -intervalSide); break;
                case Direction.NorthWest:
                    MoveImage(intervalSide, intervalSide); break;
                case Direction.NorthEast:
                    MoveImage(-intervalSide, intervalSide); break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Determines direction by giving intervals.
        /// </summary>
        /// <param name="intervalX">Horizontal interval.</param>
        /// <param name="intervalY">Vertical interval.</param>
        /// <param name="theta">Tangent of interval.</param>
        /// <returns>Direction of interval.</returns>
        private Direction IntervalToDirection(int intervalX, int intervalY, double theta)
        {
            if (intervalX < 10 && intervalX > -10 && intervalY < 10 && intervalY > -10)
                return Direction.Center;
            else if (theta < 20 && theta > -20)
            {
                if (intervalX > 0)
                    return Direction.East;
                else if (intervalX < 0)
                    return Direction.West;
            }
            else if (theta > 70 || theta < -70)
            {
                if (intervalY > 0)
                    return Direction.South;
                else if (intervalY < 0)
                    return Direction.North;
            }
            else
            {
                if (intervalX > 0 && intervalY > 0)
                    return Direction.SouthEast;
                else if (intervalX < 0 && intervalY > 0)
                    return Direction.SouthWest;
                else if (intervalX < 0 && intervalY < 0)
                    return Direction.NorthWest;
                else if (intervalX > 0 && intervalY < 0)
                    return Direction.NorthEast;
            }
            return Direction.Center;
        }

        /// <summary>
        /// Determines cursor by giving direction.
        /// </summary>
        /// <param name="direction">Direction.</param>
        /// <returns>Cursor corresponding to the direction.</returns>
        private Cursor DirectionToCursor(Direction direction)
        {
            Cursor result;
            switch (direction)
            {
                case Direction.Center:
                    result = Cursors.NoMove2D; break;
                case Direction.East:
                    result = Cursors.PanEast; break;
                case Direction.West:
                    result = Cursors.PanWest; break;
                case Direction.South:
                    result = Cursors.PanSouth; break;
                case Direction.North:
                    result = Cursors.PanNorth; break;
                case Direction.SouthEast:
                    result = Cursors.PanSE; break;
                case Direction.SouthWest:
                    result = Cursors.PanSW; break;
                case Direction.NorthWest:
                    result = Cursors.PanNW; break;
                case Direction.NorthEast:
                    result = Cursors.PanNE; break;
                default:
                    result = Cursors.Arrow; break;
            }
            return result;
        }

        #region PictureBox Event Handler Methods

        private void pictureBox_Resize(object sender, EventArgs e)
        {
            if (_image == null)
                return;

            AdjustPictureBox();
        }

        private void pictureBox_Paint(object sender, PaintEventArgs e)
        {
            if (_image == null)
                return;

            /*int areaWidth = GetImageAreaWidth();
            int areaHeight = GetImageAreaHeight();
            int barWidth = 20;
            int barHeight = 25;

            int displayWidth = _image.Size.Width;
            int displayHeight = _image.Size.Height;

            displayWidth = (int)((double)displayWidth * _imageDisplayScale);
            displayHeight = (int)((double)displayHeight * _imageDisplayScale);

            if (displayWidth <= areaWidth)
            {
                hScrollBar.Visible = false;
                pictureBox.Height = areaHeight;
            }
            else
            {
                pictureBox.Height = areaHeight - barHeight;
                hScrollBar.Visible = true;
            }

            if (displayHeight <= areaHeight)
            {
                vScrollBar.Visible = false;
                pictureBox.Width = areaWidth;
            }
            else
            {
                pictureBox.Width = areaWidth - barWidth;
                vScrollBar.Visible = true;
            }

            //Assure that the image is displayed at the correct place.
            if (displayWidth <= areaWidth)
            {
                if (displayHeight <= areaHeight)
                    _imageStartX = (areaWidth - displayWidth) / 2;
                else
                    _imageStartX = (areaWidth - displayWidth - barWidth) / 2;
            }
            else
            {
                if (_imageStartX < pictureBox.Width - displayWidth)
                    _imageStartX = pictureBox.Width - displayWidth;
                else if (_imageStartX > 0)
                    _imageStartX = 0;

                hScrollBar.Minimum = 0;
                hScrollBar.Maximum = displayWidth;
                hScrollBar.Value = -_imageStartX;
                hScrollBar.SmallChange = displayWidth / _SCROLL_BAR_TICK;
                hScrollBar.LargeChange = pictureBox.Width;
            }

            if (displayHeight <= areaHeight)
            {
                if (displayWidth <= areaWidth)
                    _imageStartY = (areaHeight - displayHeight) / 2;
                else
                    _imageStartY = (areaHeight - displayHeight - barHeight) / 2;
            }
            else
            {
                if (_imageStartY < pictureBox.Height - displayHeight)
                    _imageStartY = pictureBox.Height - displayHeight;
                else if (_imageStartY > 0)
                    _imageStartY = 0;

                vScrollBar.Minimum = 0;
                vScrollBar.Maximum = displayHeight;
                vScrollBar.Value = -_imageStartY;
                vScrollBar.SmallChange = displayHeight / _SCROLL_BAR_TICK;
                vScrollBar.LargeChange = pictureBox.Height;
            }*/

            e.Graphics.DrawImage(_image, _imageStartX, _imageStartY, _imageDisplayWidth, _imageDisplayHeight);
        }

        private void pictureBox_MouseEnter(object sender, EventArgs e)
        {
            if (_image != null)
                Cursor = Cursors.Arrow;
        }

        private void pictureBox_MouseLeave(object sender, EventArgs e)
        {
            Cursor = Cursors.Default;
        }

        private void pictureBox_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                _mouseLocation = e.Location;
                _mouseDraggingPressing = true;
            }
            else if (e.Button == MouseButtons.Right)
            {
#if (!Limit)
                //_mouseGestureParser.GestureMouseDown(sender, e);
#endif
            }
            else if (e.Button == MouseButtons.Middle)
            {
                if (!_mouseMiddlePressed)
                {
                    Cursor = Cursors.NoMove2D;
                    _mouseLocation = e.Location;
                    imageMoveTimer.Start();
                }
                else
                {
                    Cursor = Cursors.Cross;
                    imageMoveTimer.Stop();
                }
                _mouseMiddlePressed = !_mouseMiddlePressed;
            }
        }

        private void pictureBox_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && _mouseDraggingPressing)
            {
                MoveImage(e.Location.X - _mouseLocation.X, e.Location.Y - _mouseLocation.Y);
                _mouseLocation = e.Location;
            }
            else if (e.Button == MouseButtons.Right)
            {
#if(!Limit)
                //_mouseGestureParser.GestureMouseMove(sender, e);
#endif
            }
            else if (_mouseMiddlePressed)
            {
                int intervalX = e.Location.X - _mouseLocation.X;
                int intervalY = e.Location.Y - _mouseLocation.Y;

                double theta = Math.Atan((double)intervalY / (double)intervalX) / Math.PI * 180;
                _mouseMoveDistance = Math.Sqrt((double)(intervalX * intervalX + intervalY * intervalY));

                _mouseMoveDirection = IntervalToDirection(intervalX, intervalY, theta);
                Cursor = DirectionToCursor(_mouseMoveDirection);
            }
        }

        private void pictureBox_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                _mouseDraggingPressing = false;
            }
            else if (e.Button == MouseButtons.Right)
            {
#if(!Limit)
                //_mouseGestureParser.GestureMouseUp(sender, e);
                // DoShortcutOperation(_mouseGestureParser.Track);
#endif
            }
        }

        #endregion

        #region Other Event Handler Methods

        private void hScrollBar_MouseWheel(object sender, MouseEventArgs e)
        {
            if (e.Delta > 0)
                MoveImage(_IMAGE_MOVE_INTERVAL, 0);
            else
                MoveImage(-_IMAGE_MOVE_INTERVAL, 0);
        }

        private void vScrollBar_MouseWheel(object sender, MouseEventArgs e)
        {
            if (e.Delta > 0)
                MoveImage(0, _IMAGE_MOVE_INTERVAL);
            else
                MoveImage(0, -_IMAGE_MOVE_INTERVAL);
        }

        private void hScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            HScrollBar self = sender as HScrollBar;
            _imageStartX = -self.Value;
            pictureBox.Refresh();
        }

        private void vScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            VScrollBar self = sender as VScrollBar;
            _imageStartY = -self.Value;
            pictureBox.Refresh();
        }

        private void imageMoveTimer_Tick(object sender, EventArgs e)
        {
            MoveImage(_mouseMoveDistance, _mouseMoveDirection);
        }

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (_image != null)
            {
                _image.Dispose();
                _image = null;
            }

            if (_imagePath != null)
            {
                Image image = Image.FromFile(_imagePath);
                Bitmap bitmap = new Bitmap(image);
                image.Dispose();

                _image = bitmap;
                _imageWidth = _image.Width;
                _imageHeight = _image.Height;
            }
        }

        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (!e.Cancelled)
            {
                CenterForm();
                OnImageChanged(EventArgs.Empty);
            }
            else
            {
                if (FilePath.IsStaticImageFile(_imagePath))
                {
                    BackgroundWorker self = sender as BackgroundWorker;
                    self.RunWorkerAsync();
                }
            }
        }

        #endregion

        #region Event Triggers

        protected void OnImageChanged(EventArgs e)
        {
            if (ImageChanged != null)
                ImageChanged(this, e);
        }

        protected void OnScaleChanged(EventArgs e)
        {
            if (ScaleChanged != null)
                ScaleChanged(this, e);
        }

        protected void OnMouseGestureCompleted(MouseGestureEventArgs e)
        {
            if (MouseGestureCompleted != null)
                MouseGestureCompleted(this, e);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the file path of displayed image.
        /// </summary>
        [Category("Image")]
        [Description("The file path of displayed image.")]
        [DefaultValue(null)]
        public string ImagePath
        {
            get
            {
                return _imagePath;
            }

            set
            {
                LoadImage(value);
            }
        }

        /// <summary>
        /// Gets or sets the scale of displayed image to original image.
        /// </summary>
        [Category("Image")]
        [Description("The scale of displayed image to original image.")]
        [DefaultValue(1.0)]
        public double ImageScale
        {
            get
            {
                return _imageDisplayScale;
            }

            set
            {
                SetImageScaleTo(value);
            }
        }

        /// <summary>
        /// Gets the original width of displayed image.
        /// </summary>
        [Browsable(false)]
        public int ImageWidth
        {
            get
            {
                return _imageWidth;
            }
        }

        /// <summary>
        /// Gets the original height of displayed image.
        /// </summary>
        [Browsable(false)]
        public int ImageHeight
        {
            get
            {
                return _imageHeight;
            }
        }

        /// <summary>
        /// Gets or sets the color of mouse gesture track.
        /// </summary>
        [Category("Action")]
        [Description("The color of mouse gesture track.")]
        [DefaultValue(typeof(Color), "White")]
        public Color GestureColor
        {
            get
            {
                return _mouseGestureParser.TrackColor;
            }

            set
            {
                _mouseGestureParser.TrackColor = value;
            }
        }

        /// <summary>
        /// Gets or sets the width of mouse gesture track.
        /// </summary>
        [Category("Action")]
        [Description("The width of mouse gesture track.")]
        [DefaultValue(0f)]
        public float GestureWidth
        {
            get
            {
                return _mouseGestureParser.TrackWidth;
            }

            set
            {
                _mouseGestureParser.TrackWidth = value;
            }
        }

        #endregion

        #region Events

        /// <summary>
        /// Occurs when the displayed image changed.
        /// </summary>
        [Category("Image")]
        [Description("Occurs when the displayed image changed.")]
        public event EventHandler ImageChanged;

        /// <summary>
        /// Occurs when the scale of displayed image changed.
        /// </summary>
        [Category("Image")]
        [Description("Occurs when the scale of displayed image changed.")]
        public event EventHandler ScaleChanged;

        /// <summary>
        /// Occurs when a mouse gesture has been completed.
        /// </summary>
        [Category("Action")]
        [Description("Occurs when a mouse gesture has been completed.")]
        public event MouseGestureEventHandler MouseGestureCompleted;

        #endregion
    }
}